using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;
using BriansClimb.DrawableGameObjects.GameScenes;
using BriansClimb.DrawableGameObjects.ClimbingHolds;
using BriansClimb.Climber;
using BriansClimb.GameObjects;

namespace BriansClimb.GameObjects
{
    /// <summary>
    /// Manager Class that Handles the Different Actions/State Changes Relating to the Game Scenes.
    /// </summary>
    public class SceneManager : Microsoft.Xna.Framework.GameComponent
    {
        protected Game game;

        protected FarseerGames.FarseerPhysics.PhysicsSimulator physicsSimulator;

        protected GameScene previousScene;
        protected GameScene activeScene;
        protected KeyboardState oldKeyboardState;
        protected AudioManager audioManager;
        protected Cue riseUpCue;
        protected Cue sbbCue;

        protected FileManager fileManager;
        protected FileStatManager fileStatManager;

        //Game Scenes
        protected StartScene startScene;
        protected WallSelectionScene wallSelectionScene;
        protected PlayerCustomizeScene playerCustomizeScene;
        protected WallCustomizeScene wallCustomizeScene;
        protected PlayerClimbScene playerClimbScene;
        protected VictoryScene victoryScene;
        protected FailureScene failureScene;

        //Text Menus
        protected TextMenuComponent escTextMenuComponent;
        protected TextMenuComponent loadTextMenuComponent;
        protected TextMenuComponent overwriteTextMenuComponent;

        protected int previousHoldListCount;
        protected bool clicked = false;
        protected bool pressed = false;
        protected bool overwrite = false;
        
        /// <summary>
        /// Default Constructor
        /// </summary>
        /// <param name="game">Main Game Object</param>
        /// <param name="startScene">Start Scene Object</param>
        /// <param name="wallCustomizeScene">Wall Customize Scene Object</param>
        /// <param name="wallSelectionScene">Wall Selection Scene Object</param>
        /// <param name="playerCustomizeScene">Player Customize Scene Object</param>
        /// <param name="playerClimbScene">Player Climb Scene Object</param>
        /// <param name="victoryScene">Victory Scene Object</param>
        /// <param name="failureScene">Failure Scene Object</param>
        /// <param name="fileManager">File Manager Object</param>
        /// <param name="fileStatManager">File Stat Manager Object</param>
        /// <param name="escTextMenuComponent">'ESC' Menu Object</param>
        /// <param name="loadTextMenuComponent">Load Menu Object</param>
        /// <param name="physicsSimulatorIn">Physics Simulator Object</param>
        public SceneManager(Game game, StartScene startScene, WallCustomizeScene wallCustomizeScene, 
                    WallSelectionScene wallSelectionScene, PlayerCustomizeScene playerCustomizeScene, 
                    PlayerClimbScene playerClimbScene, VictoryScene victoryScene, FailureScene failureScene, 
                    FileManager fileManager, FileStatManager fileStatManager, TextMenuComponent escTextMenuComponent, 
                    TextMenuComponent loadTextMenuComponent, FarseerGames.FarseerPhysics.PhysicsSimulator physicsSimulatorIn)
            : base(game)
        {
            this.game = game;
            this.startScene = startScene;
            this.wallCustomizeScene = wallCustomizeScene;
            this.wallSelectionScene = wallSelectionScene;
            this.playerCustomizeScene = playerCustomizeScene;
            this.playerClimbScene = playerClimbScene;
            this.victoryScene = victoryScene;
            this.failureScene = failureScene;
            this.fileManager = fileManager;
            this.fileStatManager = fileStatManager;
            this.escTextMenuComponent = escTextMenuComponent;
            this.loadTextMenuComponent = loadTextMenuComponent;
            this.physicsSimulator = physicsSimulatorIn;

            audioManager = (AudioManager)game.Services.GetService(typeof(AudioManager));

            previousHoldListCount = 0;
            activeScene = startScene;
            oldKeyboardState = Keyboard.GetState();

            riseUpCue = audioManager.GetCue("Rise UP");
            riseUpCue.Play();
        }

        /// <summary>
        /// Returns the currently active game scene
        /// </summary>
        public GameScene ActiveScene
        {
            get { return activeScene; }
        }

        #region InputChecks

        /// <summary>
        /// Checks for an 'enter' key click(press & release)
        /// </summary>
        /// <returns>bool value true if clicked, false otherwise</returns>
        private bool CheckMenuClick()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            
            bool enterResult = (oldKeyboardState.IsKeyDown(Keys.Enter) &&
                keyboardState.IsKeyUp(Keys.Enter));

            oldKeyboardState = keyboardState;

            return enterResult;
        }

        /// <summary>
        /// Checks for a 'space' key click(press & release)
        /// </summary>
        /// <returns>bool value true if clicked, false otherwise</returns>
        private bool CheckSpaceClick()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            bool result = (oldKeyboardState.IsKeyDown(Keys.Space) &&
                keyboardState.IsKeyUp(Keys.Space));

            oldKeyboardState = keyboardState;

            return result;
        }

        /// <summary>
        /// Checks for a 'esc' key click(press & release)
        /// </summary>
        /// <returns>bool value true if clicked, false otherwise</returns>
        private bool CheckEscClick()
        {
            KeyboardState keyboardState = Keyboard.GetState();

            bool spaceResult = (oldKeyboardState.IsKeyDown(Keys.Escape) &&
                keyboardState.IsKeyUp(Keys.Space));

            oldKeyboardState = keyboardState;

            return spaceResult;
        }

        /// <summary>
        /// Checks for the left mouse button click(press & release).
        /// </summary>
        private void CheckLeftClick()
        {
            MouseState mouseCurrentState = Mouse.GetState();
            clicked = false;

            if (mouseCurrentState.LeftButton == ButtonState.Pressed)
                pressed = true;

            if (mouseCurrentState.LeftButton == ButtonState.Released && pressed)
            {
                clicked = true;
                pressed = false;
            }
        }

        #endregion

        /// <summary>
        /// Creates the load menu for either the Wall Customize Scene or the Wall Selection Scene.
        /// </summary>
        private void CreateLoadMenu()
        {
            //creates a list of saved files stored in the directory
            fileManager.GetListOfFileName();

            //TextMenuComponent's SetMenuItems method only accepts arrays
            string[] fileNameArray = fileManager.FileList.ToArray();

            /* Load Menu for WallCustomizeScene that is displayed after the load option
             * is clicked in the 'esc' menu
             */
            if (activeScene == wallCustomizeScene)
            {
                loadTextMenuComponent.SetMenuItems(fileNameArray);
                loadTextMenuComponent.Position = new Vector2((Game.Window.ClientBounds.Width -
                            loadTextMenuComponent.Width) / 2, 300);

                loadTextMenuComponent.Show();

                //creating the text menu for overwriting save files
                overwriteTextMenuComponent = loadTextMenuComponent;
            }

            /* Load Menu for WallSelectionScene that is displayed in the custom tab.
             * This refreshes the list with the most recent saved games.
             */
            if (activeScene == wallSelectionScene)
                wallSelectionScene.CustomLoadTab.SetMenuItems(fileNameArray);
        }

        /// <summary>
        /// Loads the Initial State for the Game Scenes.
        /// </summary>
        private void LoadInitialState()
        {
            //Start Scene's Initial State re-hides all of the other game scenes
            if (activeScene == startScene)
            {
                playerCustomizeScene.Hide();
                wallSelectionScene.Hide();
                wallCustomizeScene.Hide();
                playerClimbScene.Hide();
                victoryScene.Hide();
                failureScene.Hide();
            }

            /* Player Customize Scene's Initial State resets the left click for the 
             * next button to false. (There seems to be some sort of bug that
             * resets the left click to true.  This is a work around until
             * the bug is detected)
             */
            if (activeScene == playerCustomizeScene)
                playerCustomizeScene.nextButton.LeftButtonClick = false;

            //Wall Customize Scene's Initial State starts with empty climbing walls
            if (activeScene == wallCustomizeScene)
            {
                ResetClimbingWalls();
            }

            //Player Climb Scene's Initial State starts with empty climbing walls
            if (activeScene == playerClimbScene)
            {
                ResetClimbingWalls();
            }
        }

        /// <summary>
        /// Deletes the Climbing Hold Objects from every List that Contains Them.
        /// </summary>
        private void ResetClimbingWalls()
        {
            //holds the starting index of the climbing holds
            int index;

            if (activeScene == wallCustomizeScene)
            {
                //there are always 9 objects loaded into the components list before the holds are placed there.
                if (wallCustomizeScene.wallManager.WallManagerComponents.Count > 9)
                {
                    //calculate starting index of the first climbing hold placed on the component list
                    index = ((wallCustomizeScene.wallManager.WallManagerComponents.Count -1) -
                            (wallCustomizeScene.wallManager.ClimbingHoldList.Count - 1));

                    /*
                     * remove the climbing holds from the component list using the calculated index and
                     * the length of the climbing hold list as the range to delete from the index
                     */
                    wallCustomizeScene.wallManager.WallManagerComponents.RemoveRange(index,
                            wallCustomizeScene.wallManager.ClimbingHoldList.Count);

                    //removes and disposes of the body and geometry object associated with the climbing hold
                    foreach (ClimbingHold ch in wallCustomizeScene.wallManager.ClimbingHoldList)
                        ch.HoldBodyPart.removeSelf();

                    //clearing the climbing hold list of all holds
                    wallCustomizeScene.wallManager.ClimbingHoldList.Clear();
                }
            }

            if (activeScene == playerClimbScene)
            {
                /* there is always 3 objects loaded into the components list before the holds are placed there
                 * only reset climbing wall if it is not the first run through
                 */
                if (playerClimbScene.Components.Count > 3)
                {
                    //calculate starting index of the first climbing hold placed on the component list
                    index = ((playerClimbScene.Components.Count - previousHoldListCount) - 1);

                    /*
                     * remove the climbing holds from the component list using the calculated index and
                     * the length of the climbing hold list as the range to delete from the index
                     */
                    playerClimbScene.Components.RemoveRange(index,previousHoldListCount);

                    //removes and disposes of the body and geometry object associated with the climbing hold
                    foreach (ClimbingHold ch in playerClimbScene.ClimbingHoldList)
                        ch.HoldBodyPart.removeSelf();
                }

                //current hold list is set to previous since you only want to delete the previous holds
                previousHoldListCount = playerClimbScene.ClimbingHoldList.Count;
            }
        }

        /// <summary>
        /// Checks if the climbing wall is made up of only footholds (these walls are un-climbable)
        /// </summary>
        /// <returns>bool value true if the route is bad, false otherwise</returns>
        private bool CheckRoute()
        {
            //count variable of the number of footholds in the hold list
            int count = 0;
            bool badRoute;

            //checks the hold list to see how many footholds are on the wall to be saved
            foreach(ClimbingHold ch in wallCustomizeScene.wallManager.ClimbingHoldList)
                if (ch.HoldFriction >= 16)
                {
                    count++;
                }

            if (count == wallCustomizeScene.wallManager.ClimbingHoldList.Count)
                badRoute = true;
            else
                badRoute = false;

            return badRoute;
        }

        /// <summary>
        /// Checks to see if the current wall meets the criteria of a saveable wall.
        /// </summary>
        private void AllowSave()
        {

            fileManager.GetListOfFileName();

            //climbing wall is not empty
            if (wallCustomizeScene.wallManager.ClimbingHoldList.Count != 0)
            {
                //climbing wall does not contain all footholds
                if (CheckRoute() == false)
                {
                    //there are less than 10 saved files
                    if (fileManager.SaveCount < 10)
                    {
                        //save the climbing wall
                        fileManager.SaveFile(wallCustomizeScene.wallManager.ClimbingHoldList);
                        overwrite = false;
                        wallCustomizeScene.Show();
                    }
                    //there are 10 or more saved files
                    else
                    {
                        //activates the overwrite feature
                        overwrite = true;
                        //creates the load menu to show the saved files
                        CreateLoadMenu();
                        escTextMenuComponent.Hide();
                    }
                }
                //climbing wall contains all footholds
                else
                {

                }
            }
            //climbing wall is empty
            else
            {

            }
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            //Start Scene Menu Options
            if (activeScene == startScene)
            {
                LoadInitialState();
                CheckLeftClick();

                //start animation no longer active
                if (startScene.InMotion == false)
                {
                    //check for 'enter' click or Left mouse click
                    if (CheckMenuClick() || clicked)
                    {
                        audioManager.PlayCue("btnclick2");

                        switch (startScene.SelectedMenuIndex)
                        {
                            //Quick Play
                            case 0:
                                startScene.Hide();
                                activeScene = wallSelectionScene;
                                previousScene = startScene;
                                wallSelectionScene.QuickPlay = true;
                                wallSelectionScene.Show();
                                break;
                            //Player Customize
                            case 1:
                                startScene.Hide();
                                activeScene = playerCustomizeScene;
                                wallSelectionScene.QuickPlay = false;
                                playerCustomizeScene.Show();
                                LoadInitialState();
                                break;
                            //Wall Customize
                            case 2:
                                startScene.Hide();
                                activeScene = wallCustomizeScene;
                                wallCustomizeScene.Show();
                                LoadInitialState();
                                break;
                            //Tutorial
                            case 3:
                                break;
                            //Options
                            case 4:
                                break;
                            //Exit
                            case 5:
                                game.Exit();
                                break;
                        }
                    }
                }
            }

            //Player Customize Scene Options
            if (activeScene == playerCustomizeScene)
            {
                wallSelectionScene.Hide();

                //Back button action shows the Start Scene
                if (playerCustomizeScene.backButton.LeftButtonClick)
                {
                    audioManager.PlayCue("btnclick2");
                    activeScene = startScene;
                    startScene.Show();
                }

                //Next button action shows the Wall Selection Scene
                if (playerCustomizeScene.nextButton.LeftButtonClick || CheckMenuClick())
                {

                    audioManager.PlayCue("btnclick2");
                    previousScene = playerCustomizeScene;
                    activeScene = wallSelectionScene;
                    playerCustomizeScene.Hide();
                    wallSelectionScene.Show();

                    //setting the climber attributes based on the slider bars' final location
                    fileStatManager.weightStat = playerCustomizeScene.WeightAttribute.getFinalAttribute;
                    fileStatManager.strengthStat = playerCustomizeScene.StrengthAttribute.getFinalAttribute;
                    fileStatManager.flexibilityStat = playerCustomizeScene.FlexibilityAttribute.getFinalAttribute;
                    playerClimbScene.Climber.setClimberFriction((int)fileStatManager.climberFriction);
                    playerClimbScene.Climber.setClimberFlexibility((float)fileStatManager.ClimberFlexibility);
                    playerClimbScene.StaminaBar.ConstantDrop = fileStatManager.StaminaBarValue;
                }
            }

            //Wall Selection Scene Options
            if (activeScene == wallSelectionScene)
            {
                playerClimbScene.Hide();

                /* Back button action shows either the Start Scene or the
                 * Player Customize Scene depending on the previous scene
                 */
                if (wallSelectionScene.backButton.LeftButtonClick)
                {
                    if (previousScene == playerCustomizeScene)
                    {
                        audioManager.PlayCue("btnclick2");
                        activeScene = playerCustomizeScene;
                        playerCustomizeScene.Show();
                    }
                    
                    if(previousScene == startScene)
                    {
                        audioManager.PlayCue("btnclick2");
                        activeScene = startScene;
                        startScene.Show();
                    }
                }

                //creates a list of save files when the custom tab is selected
                if (wallSelectionScene.CustomLoadTab.Visible)
                    CreateLoadMenu();

                if (wallSelectionScene.nextButton.LeftButtonClick || CheckMenuClick())
                {
                    audioManager.PlayCue("btnclick2");

                    switch (wallSelectionScene.NameOfWindow)
                    {
                        case "easyWindow":
                            //loads preset easy climber stats
                            if (previousScene == startScene)
                            {
                                playerClimbScene.Climber.setClimberFriction(6);
                                playerClimbScene.StaminaBar.ConstantDrop = .0100f;
                            }
                            //loads preset easy climbing wall
                            fileManager.LoadFile("EasyClimb");
                            playerClimbScene.ClimbingHoldList = fileManager.FileContentList;
                            break;
                        case "mediumWindow":
                            //loads preset medium climber stats
                            if (previousScene == startScene)
                            {
                                playerClimbScene.Climber.setClimberFriction(11);
                                playerClimbScene.StaminaBar.ConstantDrop = .0150f;
                            }
                            //loads preset medium climbing wall
                            fileManager.LoadFile("MediumClimb");
                            playerClimbScene.ClimbingHoldList = fileManager.FileContentList;
                            break;
                        case "hardWindow":
                            //loads preset hard climber stats
                            if (previousScene == startScene)
                            {
                                playerClimbScene.Climber.setClimberFriction(15);
                                playerClimbScene.StaminaBar.ConstantDrop = .02f;
                            }
                            //loads preset hard climbing wall
                            break;
                        case "customWindow":
                            //loads file based on selected menu item
                            fileManager.LoadFile(fileManager.FileList[wallSelectionScene.CustomLoadTab.SelectedItemIndex]);
                            playerClimbScene.ClimbingHoldList = fileManager.FileContentList;
                            break;
                    }

                    activeScene = playerClimbScene;
                    playerClimbScene.Show();
                    LoadInitialState();

                    riseUpCue.Stop(AudioStopOptions.Immediate);
                    sbbCue = audioManager.GetCue("SBB");
                    sbbCue.Play();
                }
            }

            //Player Climb Scene Options
            if (activeScene == playerClimbScene)
            {
                //checks victory conditions
                if (playerClimbScene.HighestHoldReached && playerClimbScene.FadedToBlack)
                {
                    activeScene = victoryScene;
                    victoryScene.Show();
                }

                //checks failure conditions
                if (playerClimbScene.ClimberIsFalling && playerClimbScene.FadedToBlack)
                {
                    activeScene = failureScene;
                    failureScene.Show();
                }

                //'ESC' menu Options
                if (playerClimbScene.EscMenu.Visible)
                {
                    if (CheckMenuClick())

                        switch (playerClimbScene.EscSelectedMenuIndex)
                        {
                            //'Resume' Option
                            case 0:
                                playerClimbScene.Show();
                                break;
                            //'Main Menu' option
                            case 1:
                                activeScene = startScene;
                                startScene.Show();
                                LoadInitialState();
                                sbbCue.Stop(AudioStopOptions.Immediate);
                                riseUpCue = audioManager.GetCue("Rise UP");
                                riseUpCue.Play();
                                break;
                            //'Load' option
                            case 2:
                                activeScene = wallSelectionScene;
                                wallSelectionScene.Show();
                                sbbCue.Stop(AudioStopOptions.Immediate);
                                riseUpCue = audioManager.GetCue("Rise UP");
                                riseUpCue.Play();
                                break;
                        }
                }
            }

            //Victory Scene Options
            if (activeScene == victoryScene)
            {
                playerClimbScene.Hide();
            
                //'esc' button takes you back to start scene
                if (CheckEscClick())
                {
                    victoryScene.Hide();
                    activeScene = startScene;
                    startScene.Show();
                    
                    //resetting victory conditions
                    playerClimbScene.HighestHoldReached = false;
                }
            }

            //Failure Scene Options
            if (activeScene == failureScene)
            {
                playerClimbScene.Hide();

                //'esc' button takes you back to the start scene
                if (CheckEscClick())
                {
                    failureScene.Hide();
                    activeScene = startScene;
                    startScene.Show();

                    //resetting the failure conditions
                    playerClimbScene.ClimberIsFalling = false;
                    playerClimbScene.Climber.HasLetGo = false;
                }
            }

            //Wall Customize Scene Options
            if (activeScene == wallCustomizeScene)
            {
                //Climbing Hold Limit Warning disappears when 'spacebar' is clicked
                if (wallCustomizeScene.WarningText.Visible)
                    if (CheckSpaceClick() && wallCustomizeScene.WarningSelectedMenuIndex == 0)
                        wallCustomizeScene.Show();

                //New button action
                if (wallCustomizeScene.NewButton.LeftButtonClick)
                {
                    ResetClimbingWalls();
                    audioManager.PlayCue("btnclick2");
                }

                //'ESC' menu Options
                if (wallCustomizeScene.EscMenu.Visible)
                {
                    if (CheckMenuClick())
                        switch (wallCustomizeScene.EscSelectedMenuIndex)
                        {
                            //'Resume' option
                            case 0:
                                audioManager.PlayCue("btnclick2");
                                wallCustomizeScene.Show();
                                break;
                            //'Main Menu' option
                            case 1:
                                audioManager.PlayCue("btnclick2");
                                activeScene = startScene;
                                startScene.Show();
                                break;
                            //'Save' option
                            case 2:
                                audioManager.PlayCue("btnclick2");
                                AllowSave();
                                break;
                            //'Load' option
                            case 3:
                                audioManager.PlayCue("btnclick2");
                                overwrite = false;
                                CreateLoadMenu();
                                wallCustomizeScene.EscMenu.Hide();
                                break;
                        }
                }

                //Load Menu action
                if (loadTextMenuComponent.Visible)
                {
                    if (CheckMenuClick())
                    {
                        if (!overwrite)
                        {
                            //existing climbing holds on the climbing wall should disappear
                            ResetClimbingWalls();

                            //load the currently selected file when the 'enter' key is clicked
                            fileManager.LoadFile(fileManager.FileList[loadTextMenuComponent.SelectedItemIndex]);
                            wallCustomizeScene.wallManager.ClimbingHoldList = fileManager.FileContentList;
                            wallCustomizeScene.BPIH.setWallList(wallCustomizeScene.wallManager.ClimbingHoldList);       

                            loadTextMenuComponent.Hide();
                            wallCustomizeScene.Show();
                        }
                        //overwrite functionality
                        else
                        {
                            //delete the selected file
                            fileManager.DeleteFile(fileManager.FileList[loadTextMenuComponent.SelectedItemIndex]);

                            AllowSave();
                            loadTextMenuComponent.Hide();                    
                        }
                    }
                }
            }

            base.Update(gameTime);
        }
    }
}
